home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 1 (Walnut Creek)
/
Aminet - June 1993 [Walnut Creek].iso
/
usenet
/
sources
/
volume2
/
util
/
dresdocs
< prev
next >
Wrap
Internet Message Format
|
1988-10-25
|
59KB
Path: xanth!nic.MR.NET!hal!cwjcc!tut.cis.ohio-state.edu!mailrus!ulowell!page
From: page@swan.ulowell.edu (Bob Page)
Newsgroups: comp.sources.amiga
Subject: v02i015: dres - an object-oriented resource library (docs)
Message-ID: <9820@swan.ulowell.edu>
Date: 25 Oct 88 00:08:17 GMT
Organization: University of Lowell, Computer Science Dept.
Lines: 1588
Approved: page@swan.ulowell.edu
Submitted-by: dillon@cory.berkeley.edu (Matt Dillon)
Posting-number: Volume 2, Issue 15
Archive-name: util/dres.docs
# This is a shell archive. Remove anything before this line
# then unpack it by saving it in a file and typing "sh file"
# (Files unpacked will be owned by you and have default permissions).
# This archive contains the following files:
# ipc.doc
# lists.doc
# memory.doc
# misc.doc
# qints.doc
# res.doc
# runlib.doc
# timedate.doc
#
if `test ! -s ipc.doc`
then
echo "writing ipc.doc"
cat > ipc.doc << '\Rogue\Monster\'
IPC.DOC (DRES.LIBRARY)
Matthew Dillon
25 September 1988
Special note. I've tried my best, but this document is somewhat
lacking in the exact mechanics and implementation of IPC. Please
refer to the source module to resolve such questions.
The IPC section of the DRES.LIBRARY pertains to the sending of commands
and retrieval of results between arbitrary processes without conflict.
Processes which use IPC come in three flavors:
(1) A process which is able to accept and process IPC commands
(2) A process which might send IPC commands to other processes
(3) A process which does both.
The IPC mechanism implemented by this library is inherently asychronous
in nature (messages and ports), but also provides a synchronous call
specially tailored for cases (2) and (3). Doing IPC commands synchronously
has certain advantages, the greatest of which is that the implementation by
application software is greatly simplified. The synchronous call gets
around possible deadlocks in case (3) by interrupting itself and calling a
handler if incomming messages occur while it is waiting for the sent
message to complete.
The greatest feature of this IPC mechanism is a great flexibility in
who allocates and deallocates the message buffers. Both the source and
destination processes have a wide range of options concerning message
buffers. Unless otherwise specified, an allocated copy of the message
buffer is automatically made and used which allows the send or reply message
buffer used by the process to be immediately discarded. Additionaly, a
destination IPCPort can be flagged to force incomming messages to be
allocated no matter what the sender requests.
NOTE: The term 'allocation of messages' inherently means that if the
message is allocated, the user of the message can munge it however he likes
(exception: IF_GLOBAL messages that are propogated through all IPC ports).
Allocated messages are automatically FreeMem()d by the IPC subsystem or
by the receiver. Also, WHEN DUPLICATION OF MESSAGES OCCUR, THE ALLOCATED
MEMORY IS OF THE SAME TYPE AS THE ORIGINAL BUFFER.
NOTE: Do not get confused by the many different ways messages buffers
can be allocated. A message buffer will never be allocated-duplicate more
than once. For example, if the source specifies that it is passing an
allocated buffer, the further duplication is done by the IPC subsystem.
-The sender can allocate the message buffer himself and pass that,
pass a temporary buffer which the IPC subsystem duplicates, or pass
a static buffer to the destination without the overhead of allocation
and duplication.
-The receiver of a particular message can determine whether that message
was allocated and FreeMem() it himself, can allow the IPC subsystem
to free allocated messages, and can even force all incomming messages
on his IPC port to be allocated no matter what the sender specifies.
Forcing the IPC subsystem to allocate otherwise static messages is
a very useful ability to have. For instance, the parser employed by
the receiver of a message might destroy or modify the message buffer
during parser, while the original sender of the message might have
used a static string or otherwise expected the buffer not to be
screwed up. By the receiver being able to specify that he WANTS the
buffer to be an allocated duplicate because he intends to modify it
without permission is a good thing. Even though the sender passes
a static buffer to the IPC subsystem, that subsystem will allocate
and duplicate it for the receiver.
-In his reply to the sender, the receiver has the same sort of control
as sender in terms of how the reply message buffer is allocated. After
getting the reply back, the original sender can determine whether the
reply buffer was allocated and FreeMem() it himself, or allow the IPC
subsystem to free it when he is through with the reply.
-Finally, the receiver of the original message can even free his own
reply buffer when the original sender is through with it!
So in general, one can use either the IPC subsystem's automatic buffer
copying feature (the default), use no buffer copying at all, or use one's
own scheme.
MOST OF THESE ALLOCATION FEATURES ARE IMPLEMENTED THROUGH FLAGS. READ
THE DOCS BELOW ON OPENIPC() and SENDIPC().
APPLICATION NAME DOMAINS
An application for cases (1) or (3) must create a receiver port for
IPC messages with the OpenIPC() call. OpenIPC() takes a name and flags
argument. The Flags argument is discussed in OpenIPC() below. You do
not need a port to send messages, only to receive them.
The name of the port determines the type of messages it will receive.
The format is: <application_name>.<domain> , for example: "dmouse.CMD"
Currently, only the .CMD domain has a defined standard. It does not matter
whether multiple copies of the application are running, they can share the
same port name. You could have, say, two invocations of your favorite
editor each with five active projects. Support for this is discussed in
the ReplyIPC() call below.
While this port exists, other tasks may send IPC messages to it. You
must extract the messages (GetMsg()), process it, and then ReplyIPC() the
message to return it to the original sender.
The CloseIPC() call will shutdown an IPC port that you had previously
openning and return any pending messages by trying to pass them to the
next application of the same name (if none, an error is eventually returned
to the sender).
.CMD ports
Messages sent over .CMD ports are formatted as null-terminated
ascii-text. The send buffer (msg->TBuf) is formatted as follows:
project\0command\0 (\0 = NUL = ascii 0)
The \0 MUST be included as part of the buffer and buffer length. The
project name allows the application to identify which of its possibly
multiple projects is to be affected by the command. a NULL project name
(\0command\0) is allowed and refers to the 'active' project in the
application. The project name is ignored by applications which do not
support the notion of projects. The format of the command depends on the
application, but must be in ascii and terminate with a \0 (ascii 0).
The logical extension for the specification of multiple project names
is to allow comma delimited names and wildcards (* and ?). For example,
"*\0command\0" would refer to all projects, "a.c,b.c\0command\0" would
refer to two specific projects. This might not be supported by all
applications.
The format of the reply buffer depends on the success of the operation.
If the command was successful, the reply buffer will either be NULL or
contain some ascii/text response ending in \0. If the command was not
successful, IF_ERROR will be set in msg->RFlags and the reply buffer will
be either NULL or contain some ascii/text error message ending in \0.
The msg->Error integer error code will be non-zero if IF_ERROR was set,
possibly non-zero if IF_ERROR was not set to indicate a warning (IF_ERROR
always means Total-Failure). If msg->Error is not set by the application
which returns the error condition, it will be set by the IPC subsystem.
ParseCmd ParseCmd
argc = ParseCmd(buf, &argv, varget, varfree, &error, NULL);
int argc;
char **argv; (note, address passed)
char *((*varget)());
void (*varfree)();
long error; (note, address passed)
(note: Last NULL argument (0L) is required for future compatibility).
THIS COMMAND CAN EASILY BE USED WITH NON-IPC RELATED ROUTINES!
Do DME-type parsing on a string buffer. The buffer is terminated with
a \0 but may contain a \n at the end (which is ignored) to be compatible
with fgets() as well as gets(). NOTE that if the string is terminated
with a \<LF><NUL> i.e. "\\n\0", the backslash will override the standard
ignorance of the \n and parse it into the result.
DME-type parsing delimites fields by spaces except those enclosed in
matching parenthesis. Multiple levels of parenthesis is tracked and
the OUTERMOST SET REMOVED. Other special characters include shift-6
(^), backslash (\), and dollar ($):
form examples function
^<char> ^c ^d ^L embed a control character, case is ignored
you may not embed ^@ (0)
\<char> \\ \( \^ override the meaning of the next character,
allows embedding special characters.
$(varname) inline string variable, var name can be
anything except ')'.
$varname inline string variable, var name
restricted to alphanumeric and '_'.
Example: "echo (this (is a) test) $file/x a mess ^g \(hi! \)ug\\"
Result: argv[0] = "echo"
argv[1] = "this (is a) test"
argv[2] = "dnet:foo/x" (assumes $file == 'dnet:foo')
argv[3] = "a"
argv[4] = "mess"
argv[5] = "<CONTROL-G>" (i.e. ascii code 7)
argv[6] = "(hi!"
argv[7] = ")ug\"
argv[8] = NULL
Note: offset -1 of each argument contains a status byte. Currently,
bit 0 is defined to indicate whether the argument was paren'd or not.
This cannot otherwise be told as the highest level of parenthesis are
stripped. (argv[0][-1] & 1) == 0, (argv[1][-1] & 1) == 1.
--------
buf -\0 or \n\0 terminated buffer holding the command to parse.
The buffer is not modified and may be discarded after the
call returns.
&argv -address of a variable which will be initialized to point
to an allocated array of pointers to strings exactly the
way the main() argv works.
varget -Pointer to a subroutine which when given a \0 terminated
variable name will return a \0 terminated string or NULL
if that variable does not exist.
varget(varname)
char *varname;
This argument may be NULL indicating string variables are
not supported. The routine may return non-allocated
strings (i.e. static storage or string constants) in which
case there is no need for a varfree() function.
varfree -Pointer to a subroutine which when given the string
pointer returned by varget() will free it. This call
is made immediately after varget() is called and returns
nothing. You may pass NULL for this argument if no free
function exists (varget was NULL or returned static
strings).
&error -You must supply a pointer to a longword which will be
returned holding the error, if any, and the index where
the error occured, if any. The index is stored in the
lower 16 bits and the error # stored in the upper 16 bits:
1 << 16 = NO MEMORY
2 << 16 = STRING VARIABLE NOT FOUND (varget() returned
NULL), the lower 16 bits contain the index into
buf.
NULL -This argument is reserved for future expansion and must
be NULL (0L).
NOTE: The returned arg list may be munged however you wish, including
the pointers themselves, just as long as you pass the original
argv pointer (i.e. the entries may be munged) to FreeParseCmd().
0 is always returned on error, and error will be set properly.
Otherwise, argc is returned and error will hold 0.
The original buffer is not modified in any way.
NOTE: Since the outer set of parenthesis () are stripped, offset -1
of each arguments provides information as to whether the
argument was quoted or not. If bit 0 is set, the argument
was quoted, I.E.: (a) b c
(av[0][-1] & 1) = 1 av[0] = "a"
(av[1][-1] & 1) = 0 av[1] = "b"
(av[2][-1] & 1) = 0 av[2] = "c"
FreeParseCmd FreeParseCmd
(void) FreeParseCmd(argv)
This routine frees the storage referenced by the argv... the argv array
and the original strings are all freed. It is ok if Individual entries
in the argv array are munged.
OpenIPC OpenIPC
port = OpenIPC(name, flags)
PORT *port;
char *name;
long flags;
Create an IPC port of the specified name suitable for receiving IPC
commands on. If the port could not be created due to lack of memory,
NULL is returned, else an EXEC port is returned. The port is setup
as PA_SIGNAL and takes up a signal bit in your signal set. If the
application does not wish a signal-port, it may FreeSignal() the
signal and change the flags to something other than PA_SIGNAL.
Due to the fact that the signal was allocated for the calling task,
the port is normally usable only from the calling task. One can
change the ownership and port characterstics but always remember that
CloseIPC() can only be called from the owning task.
IF_ALLOC If IF_ALLOC is specified, *ALL* incomming message buffers
will be forced to be allocated even if the sender of the
message specifies IF_NOCOPY. I.E. you, as the receiver of
IPC messages on this port can specify this flag to guarentee
the msg->TBuf is allocated and thus modify the contents
of the buffer itself without confusing the original sender
(who may have specified a static string)
Note: However, if this message is to be passed on via
IF_GLOBAL or IF_NOTFND, you probably do not want to munge
the buffer.
IF_GLOBAL If IF_GLOBAL is specified, this port will accept global
messages. Global messages are always of the .CMD form
though the sender could easily put stuff beyond the second
\0 in the message buffer destined for specialized routines.
If not specified, IF_GLOBAL messages will NEVER propogate
through this port.
CloseIPC CloseIPC
(void) CloseIPC(port)
PORT *port;
Delete an IPC port previously obtained by OpenIPC(). The owning task
must make this call since it will FreeSignal() the allocated signal.
Note that if the port flags are set to something other than PA_SIGNAL,
no attempt will be made to free the possibly non-existant signal bit.
This call will pass any pending messages on to the next application
of the same name, and if no other exists, will return the messages
with an error. Pending messages with IF_GLOBAL set continue their
journey through the IPC ports flagged to accept IF_GLOBAL messages.
SendIPC SendIPC
msg = SendIPC(appname, buf, len, flags)
IPCMSG *msg;
char *appname;
APTR buf;
long len, flags;
This routine starts an asynchronous IPC request. It allocates and
initializes an IPCMSG and reply port and sends the message to the
destination application (appname). The IPCMSG is returned and it's
msg->RFlags holds the IF_ALLOCMSG internal flag to cause the IPCMSG
and replyport to be deallocated when you are through with the reply.
NOTE! The returned IPCMSG has already been sent to the destination
and is not owned by you. The idea is to then CheckMsg() and/or
WaitMsg() on it. The ANode filed of the IPCMSG, however, *IS* owned
by you and you may use it however you wish.
The buffer and buffer length you pass SendIPC() is used to allocate
a copy of the buffer (which is deallocated automatically when the
destination ReplyIPC()s). This means that you can discard/reuse your
buffer as soon as the call returns. However, if you specify the
IF_NOCOPY flag SendIPC() will use your buffer pointer and you must
be sure not to modify its contents until the message is returned to you.
If you specify IF_ALLOC it is assumed your buffer pointer and length
were AllocMem()d and the system will FreeMem() them when the destination
is through processing your message and calls ReplyIPC().
** See SendIPC2() for info on how to handle the message when it comes
back.
* If the 'name' is NULL, the first application in the application
list is called. This is usually used in conjunction with IF_GLOBAL
to indicate that the message should propogate to all IPC ports in the
system.
SendIPC2 SendIPC2
msg = SendIPC2(name, msg)
This routine is called by SendIPC(). This assumes you already have an
IPC message structure that you wish to use. The msg argument is
returned. The reply fields, including msg->Error and msg->Confirm,
are zerod.
The IPCMSG must be initialized as follows:
msg->Msg.mn_ReplyPort -must hold reply port for the message
msg->Msg.mn_Length -must hold length of IPCMSG structure ONLY
if you had specified the IF_ALLOCMSG in
msg->TFlags
msg->Msg.TBuf -holds the buffer pointer to the message
or NULL
msg->Msg.TLen -holds the length of the message
msg->Msg.TFlags -holds flags associated with the message:
IF_ALLOC * automatic deallocation on reply. This flag
is set automatically if you do not specify
IF_NOCOPY. If NEITHER IF_NOCOPY or IF_ALLOC
are specified, the IPC subsystem will
allocate a duplicate of your buffer, place
it in TBuf, and set this flag for you.
IF_GLOBAL * force propogate to all applications in
the system before returning to the sender.
IF_NOCOPY * force IPC system to use the buffer you
placed in TBuf. Otherwise, the IPC system
will allocate and copy your buffer into
its own and place that in TBuf.
IF_ALLOCMSG * IPC system will deallocate the IPCMSG
itself when FreeIPC() is called. This
flag is set automatically when the IPC
system has allocate the IPCMSG in the
first place.
WARNING! Upon return, msg->TBuf will not necessarily point to your
buffer, even if you specified IF_NOCOPY or IF_ALLOC.
Handling the message when it comes back
After you make the call, you must wait for the message to complete
via WaitMsg and/or CheckMsg(). Note that WaitMsg() will remove the
message from its reply port. If you do not use WaitMsg(), you must
remember to remove the message before continuing.
Then, process the reply (msg->RBuf, msg->RLen, and msg->RFlags).
The msg->RBuf IS ALLOWED TO BE NULL. msg->RFlags will indicate
any errors:
IF_ERROR|IF_NOTFND|IF_NOAPP: no application of that name exists
IF_ERROR|IF_NOTFND : application exists, but the requested
project does not exist. OR the
command does not exist for this
application.
IF_ERROR : The application exists and can handle
the project & command, but an error
occured during interpretation of the
command.
IF_ALLOC : The reply buffer was allocated. You
have the option of FreeMem()ing it
yourself or allowing the system to
do so. If you do it, you must set
RBuf = NULL.
NOTE: msg->Error does not need to be set by the receiver. If
msg->Error is 0 and IF_ERROR is set, msg->Error will be set to
20 by the IPC subsystem.
NOTE: In the case of IF_ERROR, the reply buffer might still be
non-NULL and contain an error message.
When through dealing with the reply, you MUST call FreeIPC(msg). This
routine will deallocate the reply buffer if msg->RFlags had IF_ALLOC
set by the destination and also free the message it self if msg->TFlags
contained IF_ALLOCMSG set by SendIPC() or the user before a SendIPC2().
Additionaly, if msg->Confirm was set by the destination, this function
vector will be called with the message pointer as an argument to allow
the destination to deallocate its reply. NOTE!!! If the message has
IF_GLOBAL set in msg->TFlags, msg->Confirm should NOT be touched by
receiving applications.
DoIPC2 DoIPC2
(void) DoIPC2(name, msg, collfunc, collport)
long flags
char *name;
IPCMSG *msg;
void (*collfunc)();
PORT *collport;
This is the hall mark of the IPC library and allows one to send
SEMI-SYNCHRONOUS messages while still retaining the ability to
process incomming messages on your own IPC port (case #3). The IPC
message msg must be setup as in SendIPC2(). The collision function
and collision port may be NULL if you wish.
This command sends the message and then waits for the reply. If while
waiting for the reply a message comes in on the collision port (usually
your IPC port), the collision vector will be called with the collision
port as an argument, allowing you to process incomming messages while
waiting for one you sent. By properly designing your software, you
can allow multiply stacked DoIPC2() calls.
When the message finally comes back, this call returns. The application
must still deal with its reply buffer and call IPCFree() when through.
NOTE: The collision function is called as a subroutine of this routine
and thus if it is a general command-handling routine remember that
your command interpreter might have to be synchronously reentrant.
DoIPC2() is certainly reentrant. A4 and A5 are not screwed up by
DoIPC2() so you do not have to reload your data segment base register
from the collision vector routine if using the small data model.
(Compatible with both Aztec and Lattice C).
ReplyIPC ReplyIPC
(void) ReplyIPC(msg, buf, len, flags)
IPCMSG *msg;
APTR buf; (may be NULL & len would then be 0)
long len;
long flags;
You must reply to all IPC messages you receive on your IPC port by
calling ReplyIPC(). ReplyIPC() works almost exactly like IPCSend(),
but deals with the RBuf, RFlags, and RLen fields of the IPC structure.
Normally, the IPC system will allocate a duplicate of your reply buffer
and stick that in msg->RBuf which allows you to throw away your buffer
after calling ReplyIPC(). Various flags override this feature:
IF_ALLOC - The buffer is automatically deallocated when the
source calls FreeIPC(). If this flag exists the
IPC system will not allocate a duplicate buffer.
IF_NOCOPY - The IPC system will not allocate a duplicate buffer.
Normally, (1) neither of these flags is specified, or (2) IF_ALLOC
is specified. Specifying IF_ALLOC alone means that the IPC system
assumes you have AllocMem()d the buffer yourself and want it
automatically freed when the source (original sender) calls
FreeIPC().
Specifying both IF_ALLOC and IF_NOCOPY is equivalent to specifying
just IF_ALLOC. Specifying just IF_NOCOPY means that the IPC system
will use your buffer pointer and will NOT attempt to deallocate it.
In this case, you are responsible for keeping the buffer intact
until the source calls FreeIPC(). This is possible through the
use of the msg->Confirm vector. ** you cannot use msg->Confirm
for IF_GLOBAL packets! Because there is only one vector entry and
possibly multiple users of the message.
NOTE: The buffer pointer placed in msg->RBuf will either be yours
or the allocated duplicate. msg->RBuf will be set to NULL when (if)
the system deallocates it.
If you specify IF_NOTFND, your buffer and length are completely
ignored and the message is passed on to the next application program
with the same name. IF THE ORIGINATOR SPECIFIED IF_GLOBAL WHEN HE
SENT THE MESSAGE, AND YOU DID NOT SPECIFY IF_NOTFND, the message
will be passed on to the next application anyway and your reply will be
passed on along with it. In the same way, if you were not the first
application to get this message (it was passed to you), the
msg->RBuf/RLen/RFlags fields may already have something in them. If
your reply buffer pointer is not the same address as the one already in
msg->RBuf and msg->RFlags held IF_ALLOC, the old buffer in msg->RBuf
will be deallocated before processing continues with your ReplyIPC().
** Warning, msg->RBuf will be replaced in the same manner msg->TBuf
is by an allocated duplicate unless you specify otherwise.
FreeIPC FreeIPC
(void) FreeIPC(msg)
IPCMSG *msg;
This routine must be called when the original sender of an IPC message
has sent the message, waited for the reply, removed the message from
the reply port, and processed the reply buffer. This serves to free
the message and any associated buffers as specified below:
If msg->RFlags has IF_ALLOC set the reply buffer is immediately
deallocated and msg->RBuf set to NULL.
If msg->Confirm is non-NULL the vector will be called with the IPCMSG
as an argument.
If msg->TFlags holds IF_ALLOCMSG, the msg will be FreeMem()d with the
length msg->Msg.mn_Length. IF_ALLOCMSG is not usually set by the
user... it is set by SendIPC() which allocates a message/replyport
and wants both to be deallocated on completion.
\Rogue\Monster\
else
echo "will not over write ipc.doc"
fi
if [ `wc -c ipc.doc | awk '{printf $1}'` -ne 24710 ]
then
echo `wc -c ipc.doc | awk '{print "Got " $1 ", Expected " 24710}'`
fi
if `test ! -s lists.doc`
then
echo "writing lists.doc"
cat > lists.doc << '\Rogue\Monster\'
LISTS.DOC
These are general list handling routines that should have been provided
in EXEC. In addition to basic additions, I provide support for structures
whos Nodes are not at the base of the structure. These are the infamous
'sptr's below. Such routines take a pointer to the structure, add an
offset to it to get to the Node, do the specified operation, then subtract
the offset to return to the 'sptr's actual base. In otherwords, it allows
you to deal with such structures without having to do these hacks in your
source.
GetHead GetHead
GetSucc GetSucc
node = GetHead(list)
node = GetSucc(node) (physically the same routine as GetHead()).
This routine returns the successor of a node in the list. If a list is
specified, the first node in the list is returned. NULL is returned if
the list is empty or one has reached the end of the list.
GetTail GetTail
node = GetTail(list)
This routine returns the last node in the list, or NULL if the list
is empty.
GetPred GetPred
node = GetPred(node)
This routine returns the previous node from the given node, or NULL
if we are at the head of the list.
GetHeadOff GetHeadOff
sptr = GetHeadOff(list, offset)
This routine retrieves the first node in the list and subtracts the
specified offset to get to the base of the structure (where 'offset'
is the offset into the structure where the Node has been placed rather
than placing it as the first object in the structure).
NULL is returned is the list is empty. NOTE! Unlike GetHead(), only
a valid list pointer may be specified here.
GetTailOff GetTailOff
sptr = GetTailOff(list, offset)
This routine works the same as GetHeadOff() but works on the last
node in the list.
GetSuccOff GetSuccOff
sptr = GetSuccOff(sptr, offset)
Given a structure pointer where the Node is offset from the structure
base, add the offset to the pointer, determine the successor if any,
then subtract the offset from the successor to get back to the
structure base for the successor.
NULL is returned if there is no successor.
GetPredOff GetPredOff
sptr = GetPredOff(sptr, offset)
This routine works the same as GetSuccOff() but works on the previous
node rather than the next. NULL is returned if we are at the head
of the list;
EnqueueLong EnqueueLong
(void) EnqueueLong(list, startnode, node, valoff)
This routine queues a node into a list beginning its search at the
startnode (first element if startnode == NULL). The list is assumed
to be sorted by the longword valoff from the node structure. I.E.
If you had a graphic structure with a Node at its base and where
coordinates were stored in longwords, you insert nodes sorted by
one of the coordinates into a list.
valoff is the offset from the node base where the longword value
the list is sorted by exists.
EnqueueOffLong EnqueueOffLong
(void) EnqueueOffLong(list, startnode, sptr, off, valoff)
This works just like EnqueueLong() but an extra parameter, 'off', is
provided to inform the routine where in the structure the Node is.
Does that make sense?
SearchFwdNode SearchFwdNode
retval = SearchFwdNode(node, function, arg)
MINNODE *node;
long (*function)();
long arg;
This routine searches a list in the forward direction beginning at
the specified node, calling the function vector for every node. The
function is called C-fashion (A4 and A5 are intact to support the
small code model and D2/D3 may be destroyed in addition to standard
scratch variables to support Aztec C). The function is given two
arguments: the node, and the arg passed to SearchFwdNode():
(*function)(current_srch_node, arg)
The search is terminated if the function vector returns a non-zero
value. If the search reaches the end of the list, NULL is returned.
NOTE: you may pass a NULL as the node to SearchFwdNode() which results
in a NULL being immediately returned.
SearchRvsNode SearchRvsNode
retval = SearchFwdNode(node, function, arg)
This function works exactly the same as SearchFwdNode() but works
in reverse begining at the specified node.
SearchFwdList SearchFwdList
retval = SearchFwdList(list, function, arg)
This function works exactly the same as SearchFwdNode() but you give
it a pointer to a list rather than to a node.
A valid list pointer must be specified, but the list may be empty.
SearchRvsList SearchRvsList
retval = SearchRvsList(list, function, arg)
This function works exactly the same as SearchRvsNode() but you give
it a pointer to a list rather than to a node.
A valid list pointer must be specified, but the list may be empty.
SearchFwdNodeOff SearchFwdNodeOff
SearchRvsNodeOff SearchRvsNodeOff
SearchFwdListOff SearchFwdListOff
SearchRvsListOff SearchRvsListOff
rval = SearchFwdNodeOff(sptr, function, off, arg)
rval = SearchRvsNodeOff(sptr, function, off, arg)
rval = SearchFwdListOff(list, function, off, arg)
rval = SearchRvsListOff(list, function, off, arg)
These routines work like those shown above, but an additional
argument, an offset, is supplied. This is the offset into the
structure pointer where the Node structure is embedded. In otherwords,
the other routines were simply special cases of this one with off == 0.
Note that for *NodeOff() routines a pointer to a structure containing
the Node structure in it somewhere is supplied, whereas in the
*ListOff() routines a pointer to the list base is given (this hasn't
changed).
\Rogue\Monster\
else
echo "will not over write lists.doc"
fi
if [ `wc -c lists.doc | awk '{printf $1}'` -ne 5861 ]
then
echo `wc -c lists.doc | awk '{print "Got " $1 ", Expected " 5861}'`
fi
if `test ! -s memory.doc`
then
echo "writing memory.doc"
cat > memory.doc << '\Rogue\Monster\'
MEMORY.DOC
These are general memory handling routines which will perform operations
as quickly as possible. They are super-optimized to use long word and
multiple-register moves whenever possible.
BZero BZero
(void) = BZero(buf, bytes)
APTR buf;
long bytes;
Zero out an area of memory. That was simple! This is simply a special
case of BSet()
BSet BSet
(void) = BSet(buf, bytes, value)
APTR buf;
long bytes;
long value; (only lower 8 bits used)
Jam a byte value into an area of memory.
BMov BMov
(void) = BMov(src, dest, bytes)
APTR src, dest;
long bytes;
Move a block of memory from the source to the destination. An
ascending or decending copy is used depending on how the source and
destination overlap, if at all. Both are optimized to use multiple-
register moves when possible, longword moves, or byte moves if the
two blocks are hopelessly out of alignment.
BCmp BCmp
BOOL = BCmp(src, dest, bytes)
APTR src, dest;
long bytes;
Compare two blocks of memory. (1) is returned on success, (0) on
failure. Currently, this routine is not optimized at all.
\Rogue\Monster\
else
echo "will not over write memory.doc"
fi
if [ `wc -c memory.doc | awk '{printf $1}'` -ne 1234 ]
then
echo `wc -c memory.doc | awk '{print "Got " $1 ", Expected " 1234}'`
fi
if `test ! -s misc.doc`
then
echo "writing misc.doc"
cat > misc.doc << '\Rogue\Monster\'
MISC.DOC
These are miscellanious useful routines for your enjoyment.
WildCmp WildCmp
CBOOL = WildCmp(wildcard, filename)
char *wildcard;
char *filename;
The wildcard characters supported are * and ?. The wildcard is
compared against the filename and (1) returned on success, (0) on
failure.
WaitMsg WaitMsg
msg = WaitMsg(msg)
EXECMSG *msg;
This routines Waits for a message to be returned just like WaitIO()
waits for an io request to complete. The message is REMOVED from the
reply port after it has returned.
the message must have a valid mn_ReplyPort() and must have been queued
with PutMsg(), which sets the ln_Type to NT_MESSAGE, and replied with
ReplyMsg(), which sets the ln_Type to NT_REPLYMSG.
CheckMsg CheckMsg
msg/NULL = CheckMsg(msg)
EXECMSG *msg;
This routine checks to see if the message has been returned. The
same restrictions apply as for WaitMsg(). the message is NOT removed.
The message is returned if it has been returned (not removed), or
NULL otherwise.
CheckPort CheckPort
msg/NULL = CheckPort(port)
EXECMSG *msg;
PORT *port;
This routine works like WaitPort(), but (1) does not block, and
(2) does not remove the message. If the port is not empty the first
message is returned (not removed), else NULL is returned if the port
is empty.
LockAddr LockAddr
(void) LockAddr(&lock)
long lock[2];
The lock structure (8 bytes) MUST initially be 0. This routine obtains
an exclusive lock on the structure and blocks until that lock can be
obtained. The structure must be word-aligned.
Unless it is forced to block, this subroutine is extremely fast. This
routine does not allocate any signals, but uses the reserved EXEC
semaphore signal.
LockAddrB LockAddrB
(void) LockAddr(bitno, &lock)
long bitno;
long lock[2];
The lock structure actually supports up to 8 independant exclusive
locks. LockAddr() is a special case which uses bit # 0. This call
works as in LockAddr() but you may specify the bit you wish to lock
(0 to 7). If you specify 0, this call is equivalent to LockAddr().
UnLockAddr UnLockAddr
(void) UnLockAddr(&lock)
long lock[2];
Remove an exclusive lock you had previously obtained. If other tasks
are waiting for this lock, ALL are awakened even though only one will
get the lock next. This ensures that the highest priority task will
get the lock next.
This routine is extremely fast if nobody else is waiting for the lock,
else it has to Signal() them. You MUST have previously obtained the
lock.
UnLockAddrB UnLockAddrB
(void) UnLockAddrB(bitno, &lock)
long bitno;
long lock[2];
Again, this routine works the same as UnLockAddr() with the exception
that you may specify one of the 8 bits to unlock (0 to 7).
DoSyncMsg DoSyncMsg
(void) DoSyncMsg(port, msg)
PORT *port;
EXECMSG *msg;
This routine PutMsg()s a message and waits for it to be returned. This
routine creates its own reply port on the stack and stuffs it into
mn_ReplyPort for you. Thus, virtually no setup is required to use
this routine.
FindName2 FindName2
node/NULL = FindName2(list, name)
This routine is identical to the EXEC FindName() call with the exception
that it ignores nodes whos ln_Name fields are NULL. ln_Name fields in
the list nodes must contain either NULL or a valid string pointer.
GetTaskData GetTaskData
ptr = GetTaskData(name, bytes)
APTR ptr;
char *name;
long bytes;
This routine retrieves/allocates task-private named storage. For a
specific name, the first GetTaskData() call will allocate the specified
# of bytes and zero them. Space to hold the name itself is also
allocated (i.e. you can use a temporary buffer to hold 'name' when you
make this call). Future calls return the pointer to the already
allocated storage without modifying it.
The storage is automatically freed if the TASK is removed... note that
the task is not normally removed when a C program exits back into a
CLI enviroment... it uses the CLI's task to run the program. The
task's memory list is used to implement this function.
FreeTaskData FreeTaskData
(void) FreeTaskData(name)
char *name;
If the task-private name exists, the storage associated with it is
freed. This works with memlist entries allocated with GetTaskData()
or by the user, assuming the ln_Name field points to a valid string.
(note: FreeEntry() is used after the associated MemList structure is
unlinked frlom the list. ln_Name is not specifically freed but the
way GetTaskData() works, the second entry is actually the storage
associated with the ln_Name)
\Rogue\Monster\
else
echo "will not over write misc.doc"
fi
if [ `wc -c misc.doc | awk '{printf $1}'` -ne 4956 ]
then
echo `wc -c misc.doc | awk '{print "Got " $1 ", Expected " 4956}'`
fi
if `test ! -s qints.doc`
then
echo "writing qints.doc"
cat > qints.doc << '\Rogue\Monster\'
QINTS.DOC
I call them Q-Interrupts to tell them apart from other types of
interrupts that exist on the Amiga. QInts provide a general purpose
priority enhanced local task-interrupt system based on exceptions.
Essentially, one can associate a Q interrupt with any set of EXEC
signals. When an associated signal comes in, a Q interrupt occurs. The
interrupt is taken immediately if the current task Q-priority is lower
than the Q-priority of the interrupt, else it is queued until the
priority becomes lower.
When a Q-interrupt is taken, the task's Q-priority is raised to that
of the Q-interrupt while the interrupt is being processed. Additionaly,
the application software may set the task's Q-priority at any time,
usually to prevent interrupts from occuring in critical sections.
OpenQInts OpenQInts
handle = OpenQInts()
long handle;
This routine initializes a handle for the Q interrupt system. All
32 EXEC signals may be controlled through this one handle. NULL is
returned on error, else a non-zero handle is returned.
CloseQInts CloseQInts
(void) = CloseQInts(handle)
long handle;
This routine shuts down the handle, including the removal of any
active Q Interrupt vectors.
SetQPri SetQPri
(void) = SetQPri(handle, pri)
long handle;
long pri;
This routine sets the current Q-interrupt priority for the task...
actually all interrupts associated with the handle but this is
usually all the interrupts period.
Any Q-interrupts with higher priority can occur, while interrupts
with lower or equal priority will be queued until you SetQPri() to
a lower value.
'pri' has a range -127 to 127
SetQVector SetQVector
oldvec = SetQvector(handle, vector, signo, arg, pri)
void (*oldvec)();
void (*vector)();
long handle;
long signo;
long arg;
long pri;
This routine applied or removes or changes the priority of a
Q interrupt. If vector is NULL, the interrupt is removed for the
specified signo. If vector is not NULL the interrupt is set for
the specified signo, replacing any previous interrupt vector and
priority that existed.
The interrupt has a priority of 'pri', (-127 to 127).
The vector may be a C routine. The OpenQInts() routine saved A4/A5
and these registers are automatically re-loaded before the vector
is called to support the small-data model. Additionaly, D2 and D3
Parameters are also scratch to support Aztec C. Both Aztec and Lattice
C are thus supported. Parameters to the service routine are passed on
the stack:
(*vector)(arg)
Specifically, the argument you gave SetQVector().
NOTE: SetQVector() may be called from a Q-interrupt.
\Rogue\Monster\
else
echo "will not over write qints.doc"
fi
if [ `wc -c qints.doc | awk '{printf $1}'` -ne 2789 ]
then
echo `wc -c qints.doc | awk '{print "Got " $1 ", Expected " 2789}'`
fi
if `test ! -s res.doc`
then
echo "writing res.doc"
cat > res.doc << '\Rogue\Monster\'
RESOURCE.DOC
AMIGA RESOURCES AS IMPLEMENTED BY DRES.LIBRARY
The first order of business is to avoid some confusion. The Amiga
already has resources .... EXEC resources, which are used mainly to
arbitrate low level hardware. The resources I am talking about is an
object oriented system and has nothing to do with EXEC resources. All
references to 'resources' in this document refer to this object
oriented system.
What is a resources? A resource is a means of accessing one or more
objects through a generalized interface with as much flexibility as
possible. A specific object in this system has certain capabilities
and is referenced by a pointer to something. For instance, an
intuition Window could be a resource. Each and every resource in the
system may be flagged as follows (any combination of flags):
SHARABLE - Multiple references to the resource are allowed. If not
sharable, duplication of a resource results in two
distinct resources with different data areas rather
than two pointers to a shared data area.
Duplication of non-sharable resources via DupRes()
results in a copy of that resource's data including any
changes made to that data. GetRes()ing it results in
a pristine copy.
Duplication of a shareable resource returns the common
data area whether you DupRes() it or GetRes() it.
GLOBAL - A global resource can be accessed by any task in the
system. NOTE: Private resources can still be
sharable, but only by a DupRes() call or a GetRes()
call from a task's private resource list.
Also, any resources tagged as GLOBAL in a task's private
resource list are immediately accessable to other tasks
via GetRes(). Searching other task's private lists for
global resources is always done last.
VIRTUAL - A virtual resource is one that has been algorithmically
generated. Most resources are virtual resources. For
example, an intuition Window would be a virtual resource.
Non virtual resources are the 'raw' resources on disk.
For instance, a "_List" resource would be a raw resource
but when you GetRes(name, "List") note that obviously
some conversion code is required to go from "_List" to
"List", in this case the code initializes the list
pointers.
Most of the time, the existance of one or more
algorithms (subroutines) to generate a resource is
completely transparent to the routine requesting the
resource. Using the above example, the task requesting
the resource simply asks for a "List" and is oblivious
to what actually must be performed to give him that
List.
SWAPABLE - While not being referenced (all references fall to 0),
rather than delete the resource, it should be swapped to
disk (read: any filesystem device) and then swapped
back in when accessed. This does not mean a resource
is automatically swapped when references fall to 0,
just that it *might* be swapped.
This only applies to SHARABLE resources, as you cannot
DupRes() a non-shared resource when the references fall
to 0 (nothing to dup), and GetRes() returns a pristine
copy.
SHARABLE resources on the otherhand are assumed to be
of a more permanent nature. As in the LOCKED flag
below, this flag guarentees the resource will not become
pristine on the next reference after previous usage.
LOCKED - While not being referenced, rather than delete or swap
the resource, it should stay in memory. This only
applies to SHARABLE resources.
RESOURCE NAMES AND ALGORITHMIC GENERATION
The identification of a resource consists of two parts. (1) The name
of the resource, and (2) the structure of the resource. The name of
the resource is arbitrary while the structure of the resource is some
agreed upon standard. The two parts are stuck together with an
intervening period like so:
CHArlIE.Window
For the above example, we have a resource named 'CHArlIE' of structure
'Window'... an intuition window pointer would be the result of
accessing this particular resource. However, it is not possible to
store a Window structure on disk verbatim since there are lots of
pointers and other dependancies with intuition. On disk, the resource
might actually be:
CHArlIE.NewWindow
But we want a Window structure... if the program requests
CHArlIE.Window there must be some means of algorithmically translating
a NewWindow to a Window. These algorithms are provided by yet a
third resource named:
NewWindow.Window.CODE
The name is 'NewWindow.Window' and the type is 'CODE'. This resource
is a procedure which translates a NewWindow to a Window by calling
OpenWindow(). The translation procedure is recursive, thus allowing
any manner of 'patching' for reasons given above, for maintaining
compatibility with older structures, and for convenience.
Finally, a resource is owned by a specific task. One can pass
resources around but it must be done with cooperation from the
library.
CODE resources are discussed later on. Now that you have an idea how
resources work, we shall discuss the library calls currently available:
RESOURCE LIBRARY CALLS
Beginning with the easiest of the calls and ending with the more
difficult. NOTE: Resource names are limited to 31 characters,
Resource types are limited to 31 characters.
resptr= GetRes(resnametype) char *resnametype;
This function retrieves the requested resource, doing any
translations required to get the resource into the requested
type. NULL is returned if the resource could not be found
or could not be translated to the requested type.
In-Memory private resources are searched first, then the private
resource files for the task, then In-Memory system resources, then
the global resource files for the system. Openning an already-open
resource causes one of two actions depending on whether the resource
is shared or not. If shared, the reference count is simply
incremented, otherwise a private copy of the resource is made.
Example: Win = GetRes("Charlie.Window");
resptr2 = DupRes(resptr1) APTR resptr1, resptr2;
This call duplicates a resource. If the resource is shared
resptr2 will be the same as resptr1 and the reference count will
be bumped. Otherwise, a new resource data area is allocated and
the old copied to the new.
Things like fixing pointers and such within a resource that is
physically duplicated are handled by the interface code (since
raw resources do not contain pointers, only VIRTUAL resources
will contain pointers and all VIRTUAL resources have some
interface code).
error = FreeRes(resptr)
Free a resource that you retrieved via GetRes(). Only the task
that owns the resource may free it (though several tasks may own
a resource through duplication and ownership changes). 1 is
returned on success, 0 on error.
That is, if task #1 GetRes()'s the resource and duplicates it once,
then task #2 duplicates it once, task #1 must call FreeRes() twice
and task #2 must call FreeRes() once. Ownership is strictly
tracked.
numres= FreeAllRes(task)
Free all resources associated with the specified task (NULL for
self).
error = ChownRes(resptr, fromtask, totask)
Change ownership of a resource from the source task to the
destination task. The resource must be owned by the source
task or an error will occur (0 return value). 1 is returned
on success. NULL may be specified for either or both tasks
and means the calling task.
handle= UnLinkAllRes(task)
Unlink all resources associated with the specified task (NULL
for self) and return a handle representing those resources.
This is useful for shells and such to keep their resources from
getting removed by commands they run. Combined with the NUNLINK
flag one can pass resources to a Command and keep the rest out of
reach.
(void) ReLinkAllRes(handle, task)
Link all the resources represented by the handle to the specified
task (NULL for self).
oldfl = SetResFlags(resptr, newflags, flagsmask)
Modify the flags associated with a resource. NOTE: If multiple
references to a shared resource exist, all are modified. Some
modifications may be disallowed by the system. This call is
normally used to modify the LOCKED and SWAPABLE flags (note that
the SWAPABLE flag can be changed back and forth only for resource
which support it).
error = AddRes(resnametype, flags, ptr, ctlcode);
char *resnametype;
long flags;
APTR ptr;
long (*ctlcode)();
Add a resource to the system. The resource is placed either in
the task's privately accessable in-memory resource list or
in the system global accessable in-memory resource list depending
on the specified flags. One may now GetRes() the resource.
ONLY VIRTUAL RESOURCES MAY BE ADDED IN THIS WAY. The VIRTUAL
and LOCKED flags are automatically set.
If flags specifies a VIRTUAL resource
error = RemRes(resname)
Remove the specified resource. An error will occur and the resource
will not be removed (1) if it does not exist in memory, or (2) it
is currently being referenced. (Note: the resource is removed even
if it is swapped or locked).
error = GetResInfo(resname, &flags, &bytes)
Get information on a resource. Information may not exist for a
resource if it is not currently in memory and would have to be
translated to get to the right type.
error = GetResList(wildcard, from, &av, &ac);
from: mask, bit 0 search private list
1 search system list
2 <not used>
3 search in-memory private list
4 search in-memory global list
Return an ARGC/ARGV list of resource names. Note that some names
might be duplicated if searching multiple lists. Restricting the
search to in-memory lists give resources which are already in
memory (but might be swapped out or removed at any time for those
with no references)
RESOURCE FILES
error = GetFileList(wildcard, from, &av, &ac);
from: mask, bit 0 search private list
1 search system list
2 search swap list
Return an ARGC/ARGV list of the files which match the specified
wildcard from the private list, system list, or system swap dir
list (in which case you get directory names). This list has been
allocated, and can be freed as follows:
Loop through all entries for (i = 0; i < ac; ++i)
FreeMem(av[i], strlen(av[i])+1);
Free the array itself: FreeMem(av, sizeof(char *) * (ac+1));
num = AddPrivResFile(filename, pri)
Add a file name to the list of resource files for this task. These
are scanned before global files when a resource is requested. All
files in the list are write protected (i.e. a shared lock is kept
for each file). Thus, such files cannot be updated until removed
from the list.
Can also be used to modify the priority of an existing file
num = RemPrivResFiles(wildcard)
Remove zero or more file names from the list of resource files for
this task. A wildcard pattern (* and ?) is accepted.
Note for command processors: commands you run might execute
this command for *.
num = AddGlobResFile(filename, pri)
Same as AddPrivResFile() but applies to the system list, which is
searched last and by any requesting task. Wildcard file names are
NOT accepted. The file need not exist at this time, and references
to unmounted volumes are allowed.
Can also be used to modify the priority of an existing entry
num = RemGlobResFiles(wildcard)
Remove zero or more resource files from the global list. Again,
a wildcard filename is accepted.
num = AddResSwapDir(dirname, pri, maxkbytes) char *dirname;
char pri;
long maxkbytes
Add a directory to the list of directories the resource system
can swap to. The maximum number of KBytes of material allowed
in the directory should be specified. You can also use this
call to modify the priority and maxkbytes for an entry.
The highest priority directories are used before lower priority
directories. Not all directories need be mounted, but if a swapin
occurs from an unmounted directory a requester will appear.
A lock is kept on each specified directory.
num = RemResSwapDirs(wildcard)
Remove directories associated with the resource swap areas.
RESOURCE FILE IFF FORMAT
\Rogue\Monster\
else
echo "will not over write res.doc"
fi
if [ `wc -c res.doc | awk '{printf $1}'` -ne 12714 ]
then
echo `wc -c res.doc | awk '{print "Got " $1 ", Expected " 12714}'`
fi
if `test ! -s runlib.doc`
then
echo "writing runlib.doc"
cat > runlib.doc << '\Rogue\Monster\'
DRES.LIBRARY
OVERVIEW OF FUNCTIONS
This document is meant to give a simple overview of library functions,
not to provide complete explanations. Please refer to the separate
document files for various command groups for more detailed information.
* All function arguments are 32 bits unless otherwise specified
* Any Address register may be used to hold the library base pointer
when calling library routines from assembly.
IPC
ipcport = OpenIPC(name, 0)
(void) = CloseIPC(ipcport)
ipcmsg = SendIPC(name, buf, len, flags)
ipcmsg = SendIPC2(name, ipcmsg)
(void) = ReplyIPC(msg, buf, len, flags)
(void) = FreeIPC(msg)
(void) = DoIPC2(name, msg, handler, ipcport)
argc = ParseCmd(str, &argv, varget, varfree, &error, NULL)
FreeParseCmd(argv)
QINTS
handle = OpenQInts()
(void) = CloseQInts(handle)
(void) = SetQPri(handle, pri)
oldvect = SetQVector(handle, vector, signo, arg, pri)
MISC
BOOL = WildCmp(wildcard, filename)
msg = WaitMsg(msg)
msg/NULL= CheckMsg(msg)
msg/NULL= CheckPort(port)
(void) = LockAddr(&varlock) (long varlock[2] = { 0,0 })
(void) = LockAddrB(bitno, &varlock)
(void) = UnLockAddr(&varlock)
(void) = UnLockAddrB(bitno, &varlock)
(void) = DoSyncMsg(port, msg) (do not setup reply port for message)
node/NULL= FindName2(list, name) (ignores NULL ln_Name fields)
ptr = GetTaskData(name, bytes)
(void) = FreeTaskData(name)
LISTS
node = GetHead(list)
node = GetTail(list)
node = GetSucc(list/node)
node = GetPred(node)
sptr = GetHeadOff(list, offset)
sptr = GetTailOff(list, offset)
sptr = GetSuccOff(sptr, offset)
sptr = GetPredOff(sptr, offset)
(void) = EnqueueLong(list, start, node, valoff)
(void) = EnqueueOffLong(list, start, sptr, off, valoff)
rval = SearchFwdNode(node, function, arg)
rval = SearchRvsNode(node, function, arg)
rval = SearchFwdList(list, function, arg)
rval = SearchRvsList(list, function, arg)
rval = SearchFwdNodeOff(sptr, function, off, arg)
rval = SearchRvsNodeOff(sptr, function, off, arg)
rval = SearchFwdListOff(list, function, off, arg)
rval = SearchRvsListOff(list, function, off, arg)
>> function(sptr:4(sp)orA2, arg:8(sp)orD4)
MEMORY
(void) = BZero(buf, bytes)
(void) = BSet(buf, bytes, char)
(void) = BMov(src, dest, bytes)
BOOL = BCmp(src, dest, bytes) 0=failed, 1=success
TIMEDATE
DOSBOOL = SetFileDate(file, date) 0=failed, -1=success
char * = DateToS(datestamp, buf, format)
RESOURCES (NOT IMPLEMENTED YET)
\Rogue\Monster\
else
echo "will not over write runlib.doc"
fi
if [ `wc -c runlib.doc | awk '{printf $1}'` -ne 2799 ]
then
echo `wc -c runlib.doc | awk '{print "Got " $1 ", Expected " 2799}'`
fi
if `test ! -s timedate.doc`
then
echo "writing timedate.doc"
cat > timedate.doc << '\Rogue\Monster\'
TIMEDATE.DOC
Useful routines...
SetFileDate SetFileDate
DOSBOOL = SetFileDate(file, date)
char *file;
DATESTAMP *date;
This routine sets the datestamp for a file by implementing the new
ACTION_SETDATE Dos packet. 0 is returned on failure, non-zero (-1)
on success.
NOTE: The routine will fail if the file has been locked, shared or
otherwise.
DateToS DateToS
buf = DateToS(date, buf, format)
DATESTAMP *date;
char *buf;
char *format;
This routine calculates the ascii time and date from the datestamp and
jams it into the output buffer (which is returned) according to the
specified format string. If the format string is NULL, "D M Y h:m:s"
is used. Essentially, the characters D,M,Y,h,m, and s are replaced by
their ascii date equivalents (day, month, year, hours, minutes, seconds)
in the output buffer, with all other characters copied verbatim.
\Rogue\Monster\
else
echo "will not over write timedate.doc"
fi
if [ `wc -c timedate.doc | awk '{printf $1}'` -ne 968 ]
then
echo `wc -c timedate.doc | awk '{print "Got " $1 ", Expected " 968}'`
fi
echo "Finished archive 1 of 1"
# if you want to concatenate archives, remove anything after this line
exit
--
Bob Page, U of Lowell CS Dept. page@swan.ulowell.edu ulowell!page
Have five nice days.